package org.example.inventory.util;


import org.example.inventory.config.DruidConfig;
import org.example.inventory.exception.JdbcException;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * JDBC操作工具类（线程安全）
 */
public class JdbcUtil {
    // 使用ThreadLocal创建线程本地变量保存数据库连接对象
    // 好处：可以不需要关心多线程并发时的线程安全问题
    private static final ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
    // 重新配置类拿到连接池
    private static final DataSource dataSource = DruidConfig.getDataSource();

    /**
     * 获取数据库连接
     */
    public static Connection getConnection() throws SQLException {
        // 从threadLocal（线程私有区域）中取出数据库连接对象
        Connection conn = threadLocal.get();
        if (conn == null || conn.isClosed()) {
            // 如果为空，说明该线程的私有区域中没有连接对象，从连接池中获取一个连接
            conn = dataSource.getConnection();
            // 并把该连接放入线程的私有空间中
            threadLocal.set(conn);
        }
        return conn;
    }

    /**
     * 开启事务
     */
    public static void beginTransaction() throws SQLException {
        getConnection().setAutoCommit(false);
    }

    /**
     * 提交事务
     */
    public static void commitTransaction() throws SQLException {
        getConnection().commit();
    }

    /**
     * 回滚事务
     */
    public static void rollbackTransaction() throws SQLException {
        getConnection().rollback();
    }

    

    /**
     * 设置预编译参数
     * 入参1：预编译执行对象ps
     * 入参2：动态的Object类型参数params
     */
    private static void setParameters(PreparedStatement ps, Object... params)
            throws SQLException {
        // 如果params 数组为null，直接返回，不填充参数
        if (params == null) return;
        // 遍历params数组
        for (int i = 0; i < params.length;i ++){
            // 取出每个params参数
            Object param = params[i];
            // 填充参数
            ps.setObject(i + 1, param);
        }
    }

    /**
     * 封装执行DML操作的方法（INSERT/UPDATA/DELETE）
     * 入参1：SQL语句
     * 入参2：动态参数
     */
    public static int executeUpdate(String sql, Object... params) {
        // 获取连接，预编译SQL语句
        try(Connection conn = getConnection();
            PreparedStatement ps = conn.prepareStatement(sql)) {
            // 调用方法填充sql参数
            setParameters(ps, params);
            // 返回执行结果
            return ps.executeUpdate();
        } catch (SQLException e) {
            // 如果发生DQL异常，抛出自定义的Jdbc异常
            throw new JdbcException("执行更新失败", e);
        }
    }

    /**
     * 执行查询并返回单个结果
     */
    public static <T> T executeQuerySingle(String sql, ResultSetHandler<T> handler,Object... params) {
        try (Connection conn = getConnection();
            PreparedStatement ps = conn.prepareStatement(sql)) {

            setParameters(ps, params);
            try (ResultSet rs = ps.executeQuery()) {
                return rs.next() ? handler.handle(rs) : null;
            }
        } catch (SQLException e) {
            throw new JdbcException("执行查询失败", e);
        }
    }

    /**
     * 执行查询返回结果列表
     */
    public static <T> List<T> executeQueryList(String sql, ResultSetHandler<T> handler, Object... params) {
        List<T> list = new ArrayList<>();
        try (Connection conn = getConnection();
            PreparedStatement ps = conn.prepareStatement(sql)) {

            setParameters(ps, params);
            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    list.add(handler.handle(rs));
                }
            }
            return list;

        } catch (SQLException e) {
            throw new JdbcException("执行查询失败", e);
        }
    }
}
